/* 
 * File:   mpi_ub6.cpp
 * Author: sergey
 */

#include "DefineConfiguration.h"
#include <iostream>
#include <sstream>
#include <fstream>
#include <vector>
#include "LibraryInterface.h"
#include <string>

#include "tbb/task_scheduler_init.h"
#include "tbb/parallel_for.h"
#include "tbb/blocked_range.h"
#include "tbb/concurrent_queue.h"

#include "min7/genmin7v0.0.cpp"

#include <stdlib.h>
#include <signal.h>

int rank = 0, size = 1;

struct UInt128 {
    UInt64 l, r;
};

const UInt8 fComplication = 25;

UInt64 doneFunctions = 0,
       doneFunctionsP = 0;

std::ofstream outputFile;

bool term_flag = false;

void term_handler(int i) {
    std::cout << "Terminating..." << std::endl;
    term_flag = true;
}

UInt64 get_iFs() {
    std::ifstream input("config.txt");
    if (!input.is_open()) return 0;
    UInt64 ifs;
    input >> ifs;
    input.close();
    return ifs;
}

tbb::concurrent_queue<std::pair<UInt64, UInt64> > resultQueue;

class MinimizeFunction {
private:
    UInt64 lnf;
    const std::vector<UInt64> &lpclasses;

public:
    MinimizeFunction(UInt64 l, const std::vector<UInt64> &lpc) : lnf(l), lpclasses(lpc)
    { }

    void operator() (const tbb::blocked_range<size_t>& r) const {
        tf7_exp p;
        UInt64 rnf;
        for(size_t i = r.begin(); i != r.end(); ++i) {
            std::cout << i << ' ';
//            rnf = lpclasses[i];


            if (genmin7(
                    lnf >> 32, lnf & 0xFFFFFFFF, rnf >> 32, rnf & 0xFFFFFFFF, p)
                        >= fComplication)
            {
                resultQueue.push(std::pair<UInt64,UInt64>(lnf, rnf));
            }
        }
        std::cout << std::endl;
    }
};

int main(int argc, char** argv) {
    tbb::task_scheduler_init init;
    std::ostringstream filename;

    signal(SIGTERM, term_handler);

    filename << LIBRARYDIRECTORIES << "/nllibs/nllibrary_" << Lib5::n << ".bin";
    nllib.load(filename.str());
    filename.str("");

    filename << LIBRARYDIRECTORIES << "/6_14/lpclasses14-15.txt";
    std::ifstream inputFile(filename.str().c_str());
    filename.str("");

    std::vector<UInt64> lpclasses;
    while(!inputFile.eof()) {
        UInt64 t;
        inputFile >> std::hex >> t;
        std::cout << std::hex << t << std::endl;
        lpclasses.push_back(t);
    }
    inputFile.close();

    filename << LIBRARYDIRECTORIES << "/7/ub7_25-26.bin";
    outputFile.open(filename.str().c_str(), std::ios::binary | std::ios_base::app);
    filename.str("");

    filename << LIBRARYDIRECTORIES << "/6_14/full14-15.bin";
    inputFile.open(filename.str().c_str(), std::ios::binary);
    filename.str("");

    UInt64 inputFileSize;
    inputFile.seekg(0, std::ios::end);
    inputFileSize = inputFile.tellg() / 8;

    UInt64 iFs_start = get_iFs();
    inputFile.seekg(iFs_start * 8, std::ios::beg);

    std::cout << "Starting from function #" << iFs_start * lpclasses.size() << std::endl;
    
    UInt64 lnf;
    for(UInt64 iFs = iFs_start; iFs < inputFileSize; iFs++) {
        if (inputFile.eof()) break;
        if (term_flag) {
            outputFile.close();

            outputFile.open("config.txt");
            outputFile << iFs;
            outputFile.close();

            std::cout << "Processed functions " << std::dec << doneFunctions << '(' << doneFunctionsP << ')' << std::endl;
            exit(EXIT_SUCCESS);
        }
        inputFile.read((char*)&lnf, sizeof(UInt64));
        std::cout << lpclasses.size() << std::endl;
        tbb::parallel_for(
            tbb::blocked_range<size_t>(0, lpclasses.size(), 4),
            MinimizeFunction( lnf, lpclasses )
        );
        doneFunctions += lpclasses.size();
        doneFunctionsP += resultQueue.unsafe_size();
        std::pair<UInt64, UInt64> resQ;
        while (resultQueue.try_pop(resQ)) {
             outputFile.write((char*)&resQ.first, sizeof(UInt64));
             outputFile.write((char*)&resQ.second, sizeof(UInt64));
        }

        if ((iFs & 0xFF) == 0)
            std::cout << "Processed functions " << std::dec << doneFunctions << '(' << doneFunctionsP << ')' << std::endl;
    }
    inputFile.close();
    outputFile.close();
    std::cout << "Jobs'have been finished." << std::endl;
    return (0);
}
